package cn.com.joyplus.wechat.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.lang.reflect.Type;
import java.net.*;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;

/**
 * Created by 王灏川(wangrs@qq.com) on 2017/8/13 0013
 */
@Service
@SuppressWarnings("unchecked")
public class RemoteResourceService {

    private static final Logger logger = LoggerFactory.getLogger(RemoteResourceService.class);

    @Value("${httpproxy.type:DIRECT}")
    private String proxyType;

    @Value("${httpproxy.host:}")
    private String proxyHost;

    @Value("${httpproxy.port:80}")
    private Integer proxyPort;

    @Value("${httpproxy.user:}")
    private String proxyUser;

    @Value("${httpproxy.pwd:}")
    private String proxyPwd;

    String getRemoteResource(String urlPath) throws Exception {
        return getRemoteResource(urlPath, null, "GET");
    }

    String getRemoteResource(String urlPath, Map<String, String> param) throws Exception {
        return getRemoteResource(urlPath, param, "GET");
    }


    String getRemoteResource(String urlPath, Object params, String method) throws Exception {
        HttpURLConnection connection = null;
        StringBuilder sb = new StringBuilder();
        try {
            URL url;
            String reqParam = buildParams(params);

            method = (null == method || method.toUpperCase().equals("GET")) ? "GET" : method.toUpperCase();

            if ("GET".equals(method.toUpperCase()) && null != reqParam && reqParam.length() > 0) {
                url = new URL(String.format("%s?%s", urlPath, reqParam));
            } else {
                url = new URL(urlPath);
            }


            connection = openConnection(url);
            connection.setUseCaches(false);
            connection.setRequestMethod(method);
            connection.setRequestProperty("ContentType", "text/xml;charset=utf-8");
            if (null != reqParam && "POST".equals(method.toUpperCase())) {
                connection.setDoOutput(true);// 是否输入参数

                byte[] bypes = reqParam.getBytes("UTF-8");
                connection.getOutputStream().write(bypes);// 输入参数

            }
            connection.connect();
            logger.info(String.format("Send %s request for  %s.", method, url.getPath()));
            InputStream inputStream = connection.getInputStream();
            Reader reader = new InputStreamReader(inputStream, "UTF-8");
            BufferedReader bufferedReader = new BufferedReader(reader);
            String str;

            while ((str = bufferedReader.readLine()) != null) {
                sb.append(str);
            }
            reader.close();
        } finally {
            if (connection != null) connection.disconnect();
        }

        return sb.toString();
    }

    private String buildParams(Object params) throws IOException {
        if (null != params) {
            if (isMapType(params)) {
                StringBuilder reqParam = new StringBuilder();
                for (Map.Entry<String, String> entry : ((Map<String, String>) params).entrySet()) {
                    if (reqParam.length() > 0) reqParam.append("&");
                    reqParam.append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(), "utf-8"));
                }
                return reqParam.toString();
            }else if(params instanceof String){
                return params.toString();
            }else {
                StringWriter str = new StringWriter();
                ObjectMapper objectMapper = new ObjectMapper();
                objectMapper.writeValue(str, params);
                return str.toString();
            }
        }
        return null;
    }

    private Boolean isMapType(Object params) {
        boolean result = false;
        Type[] interfaces = params.getClass().getGenericInterfaces();
        if (null != interfaces && interfaces.length > 0) {
            for (Type type : interfaces) {
                if (null != type && type.toString().startsWith("java.util.Map")) {
                    result = true;
                    break;
                }
            }
        }
        return result;
    }

    private HttpURLConnection openConnection(URL url) throws NoSuchAlgorithmException, KeyManagementException, IOException {

        Proxy proxy = null;
        if (!Proxy.Type.DIRECT.toString().equals(proxyType)) {
            InetSocketAddress addr = new InetSocketAddress(proxyHost, proxyPort);
            proxy = new Proxy(Proxy.Type.valueOf(proxyType), addr);
        }

        HttpURLConnection urlConnection=null;
        if (url.getProtocol().toLowerCase().equals("https")) {
            SSLContext sc = getNoTrustSSLContext();
            if (null != sc) {
                HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
                HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> {
                    return true;//不校验 全返回true

                });

                urlConnection = (null == proxy ? (HttpsURLConnection) url.openConnection() : (HttpsURLConnection) url.openConnection(proxy));
                ((HttpsURLConnection) urlConnection).setSSLSocketFactory(sc.getSocketFactory());
            }
        } else {
            urlConnection = (null == proxy ? (HttpURLConnection) url.openConnection() : (HttpURLConnection) url.openConnection(proxy));
        }
        if (null != proxy && !StringUtils.isEmpty(proxyUser)) {
            String password = String.format("%s:%s", proxyUser, StringUtils.isEmpty(proxyPwd) ? "" : proxyPwd);
            String encodedPassword = new Base64().encodeToString(password.getBytes());
            urlConnection.setRequestProperty("Proxy-Authorization", "Basic " + encodedPassword);
        }
        return urlConnection;
    }


    private SSLContext getNoTrustSSLContext() throws KeyManagementException, NoSuchAlgorithmException {
        // 创建一个不检验证书链的信任库

        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[]{};
                    }

                    // @Override

                    public void checkClientTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws CertificateException {
                    }

                    // @Override

                    public void checkServerTrusted(java.security.cert.X509Certificate[] arg0, String arg1) throws CertificateException {
                    }
                }
        };

        // 加载不检验证书链的信任管理器

        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(null, trustAllCerts, new SecureRandom());

        return sc;
    }
    /*

    private SSLContext getTrustSSLContext() {

        try {

            InputStream is = new FileInputStream("d:/google.cer");

            CertificateFactory cf = CertificateFactory.getInstance("X.509");

            X509Certificate caCert = (X509Certificate) cf.generateCertificate(is);



            TrustManagerFactory tmf = TrustManagerFactory

                    .getInstance(TrustManagerFactory.getDefaultAlgorithm());

            KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());

            ks.load(null); // You don't need the KeyStore instance to come from a file.

            ks.setCertificateEntry("caCert", caCert);



            tmf.init(ks);



            SSLContext sslContext = SSLContext.getInstance("TLS");

            sslContext.init(null, tmf.getTrustManagers(), null);

            return sslContext;

        } catch (Exception e) {

            e.printStackTrace();

            return null;

        }

    }*/

}